import * as THREE from "three";
import { Particle } from "./Particle";

/**
 * 约束接口，所有约束需实现 apply 方法
 */
export interface IConstraint {
    enabled: boolean;
    /**
     * 对单个粒子应用约束
     */
    apply(particle: Particle): void;
    update( particles: Iterable<Particle>): void;
}

/**
 * 示例：地面碰撞约束（y=0为地面）
 */
export class GroundConstraint implements  IConstraint {
    enabled = true;
    restitution: number; // 反弹系数
    baseHeight= 0; // 地面高度
    constructor(restitution = 0.8,baseHeight = 0) {
        this.restitution = restitution;
        this.baseHeight = baseHeight;
    }

    apply(particle: Particle): void {
        if (particle.position.y < this.baseHeight) {
            particle.position.y = this.baseHeight;
            if (particle.velocity.y < 0) {
                particle.velocity.y *= -this.restitution;
            }
        }
    }
    update(particles: Iterable<Particle>): void {
        for (let particle of particles) {
            this.apply(particle);
        }
    }
}


/**
 * 通用平面约束
 * @param planeNormal 平面法向量（会自动归一化）
 * @param planeConstant 平面常数（平面方程 n·x + d = 0）
 * @param restitution 反弹系数（能量损失，0~1，1为完全弹性）
 * @param friction 摩擦系数（0~1，1为完全摩擦，0为无摩擦）
 */
export class PlaneConstraint implements IConstraint {
    enabled = true;
    normal: THREE.Vector3;
    constant: number;
    restitution: number;
    friction: number;

    constructor(
        planeNormal: THREE.Vector3,
        planeConstant: number = 0,
        restitution: number = 0.8,
        friction: number = 0.2
    ) {
        this.normal = planeNormal.clone().normalize();
        this.constant = planeConstant;
        this.restitution = restitution;
        this.friction = friction;
    }

    apply(particle: Particle): void {
        // 点到平面距离
        const dist = this.normal.dot(particle.position) + this.constant;
        if (dist < 0) {
            // 修正位置到平面上
            particle.position.addScaledVector(this.normal, -dist);

            // 速度分解
            const v = particle.velocity;
            const vN = this.normal.clone().multiplyScalar(v.dot(this.normal)); // 垂直分量
            const vT = v.clone().sub(vN); // 切向分量

            // 垂直分量反弹（能量损失）
            vN.multiplyScalar(-this.restitution);

            // 切向分量摩擦
            vT.multiplyScalar(1 - this.friction);

            // 合成新速度
            particle.velocity.copy(vN.add(vT));
        }
    }
    update( particles: Iterable<Particle>): void {
        for (let particle of particles) {
            this.apply(particle);
        }
    }
}